﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Frontend
{
    public class DateAndTimeFilterEventArgs : EventArgs
    {
        public DateTimeFilterPanel Source;
        public Boolean IsFilterEnabled;
        public DateTime DateBegin;
        public DateTime DateEnd;
        public TimeSpan TimeBegin;
        public TimeSpan TimeEnd;
    }

    /// <summary>
    /// Interaction logic for DateTimeFilterPanel.xaml
    /// </summary>
    public partial class DateTimeFilterPanel : UserControl, IStateSerializable
    {
        enum Duration { Days1 = 1, Days2 = 2, Days3 = 3, Days4 = 4, Days5 = 5, Days6 = 6,
                        Weeks1 = 7, Weeks2 = 14, Weeks3 = 21, Weeks4 = 28 };

        static readonly int OpenHour = 8;
        static readonly int CloseHour = 22;
        static readonly int DeltaHour = 1;

        static readonly int MorningBegin = OpenHour;
        static readonly int MorningEnd = 12;

        static readonly int MiddayBegin = MorningEnd;
        static readonly int MiddayEnd = 18;

        static readonly int EveningBegin = MiddayEnd;
        static readonly int EveningEnd = CloseHour;

        static readonly Brush EnabledTimeForeground = Brushes.Black;
        static readonly Brush DisabledTimeForeground = Brushes.LightGray;

        static DateTime CurrentWeekMonday
        {
            get
            {
                int dayIndex = (int)DateTime.Today.DayOfWeek;
                if (dayIndex == 0) dayIndex = 7;//sunday should be 7th, not 0th
                return DateTime.Today - TimeSpan.FromDays(dayIndex - 1);
            }
        }

        void FillDurations()
        {
            //what durations we support?
            int[] durations = (int[])Enum.GetValues(typeof(Duration));

            foreach (int dayDuration in durations)
            {
                TimeSpan duration = TimeSpan.FromDays(dayDuration);
                String name;

                if (duration.Days < 7)
                {
                    name = String.Format("{0} day{1}", duration.Days,
                                                       duration.Days > 1 ? "s" : "");
                }
                else
                {
                    int weeks = duration.Days / 7;

                    name = String.Format("{0} week{1}", weeks, weeks > 1 ? "s" : "");
                }

                ComboBoxItem item = new ComboBoxItem();
                item.Content = name;
                item.Tag = duration;
                DateDurationComboBox.Items.Add(item);            
            }
        }
        void FillTimes(ComboBox target, TimeSpan begin, TimeSpan end, TimeSpan delta)
        {
            TimeSpan cur = begin;
            while (cur <= end)
            {
                string title = String.Format("{0}:{1}", cur.Hours.ToString("00"),
                                                        cur.Minutes.ToString("00"));

                ComboBoxItem item = new ComboBoxItem();
                item.Content = title;
                item.Tag = cur;
                target.Items.Add(item);

                cur = cur + delta;                
            }
        }
        
        void SelectDuration(TimeSpan duration)
        {
            ComboBoxItem item = (from ComboBoxItem n in DateDurationComboBox.Items
                                where duration == (TimeSpan)(n.Tag)
                                select n).FirstOrDefault();
            if (item == null)
            {
                throw new ArgumentException("duration");
            }

            item.IsSelected = true;
        }
        void SelectDuration(Duration duration)
        {
            SelectDuration(TimeSpan.FromDays((double)duration));
        }
        void SelectTime(ComboBox target, TimeSpan time)
        {
            ComboBoxItem item = (from ComboBoxItem n in target.Items
                                 where time == (TimeSpan)n.Tag
                                 select n).FirstOrDefault();

            if (item == null)
            {
                throw new ArgumentException("time");
            }

            item.IsSelected = true;
        }

        public DateTimeFilterPanel()
        {
            InitializeComponent();

            FillDurations();
            SelectDuration(Duration.Weeks2);
                       

            FillTimes(TimeBeginComboBox, TimeSpan.FromHours(OpenHour), 
                                         TimeSpan.FromHours(CloseHour-1), 
                                         TimeSpan.FromHours(DeltaHour));

            SelectTime(TimeBeginComboBox, TimeSpan.FromHours(OpenHour));

            FillTimes(TimeEndComboBox, TimeSpan.FromHours(OpenHour),
                                        TimeSpan.FromHours(CloseHour),
                                        TimeSpan.FromHours(DeltaHour));

            SelectTime(TimeEndComboBox, TimeSpan.FromHours(CloseHour));

            DateBeginPicker.SelectedDateChanged += DateBeginPicker_SelectedDateChanged;
            DateDurationComboBox.SelectionChanged += DateDurationComboBox_SelectionChanged;
            TimeBeginComboBox.SelectionChanged += TimeBeginComboBox_SelectionChanged;
            TimeEndComboBox.SelectionChanged += TimeEndComboBox_SelectionChanged;
        }        

        public DateTime DateBegin
        {
            get
            {
                return DateBeginPicker.SelectedDate ?? DateTime.Today;
            }
            private set
            {
                DateBeginPicker.SelectedDate = value;
            }
        }
        TimeSpan DateDuration
        {
            get
            {
                ComboBoxItem item = (ComboBoxItem)DateDurationComboBox.SelectedItem;
                return (TimeSpan)item.Tag;
            }
            set
            {
                SelectDuration(value);
            }
        }
        public DateTime DateEnd
        {
            get
            {
                return DateBegin + DateDuration - TimeSpan.FromDays(1);
            }
            private set
            {
                DateDuration = value - DateBegin + TimeSpan.FromDays(1);
            }
        }

        public TimeSpan TimeBegin
        {
            get
            {
                ComboBoxItem item = TimeBeginComboBox.SelectedItem as ComboBoxItem;
                return (TimeSpan)item.Tag; 
            }
            private set
            {
                SelectTime(TimeBeginComboBox, value);
            }
        }
        public TimeSpan TimeEnd
        {
            get
            {
                ComboBoxItem item = TimeEndComboBox.SelectedItem as ComboBoxItem;
                return (TimeSpan)item.Tag;
            }
            private set
            {
                SelectTime(TimeEndComboBox, value);
            }
        }

        private Boolean IsChangeIgnored = false;

        public event EventHandler<DateAndTimeFilterEventArgs> Changed;
        protected void OnChanged()
        {
            if (IsChangeIgnored) return;

            if (Changed != null)
            {
                DateAndTimeFilterEventArgs args = new DateAndTimeFilterEventArgs();
                args.Source = this;
                args.IsFilterEnabled = UseManualFilterCheckbox.IsChecked ?? false;
                args.DateBegin = DateBegin;
                args.DateEnd = DateEnd;
                args.TimeBegin = TimeBegin;
                args.TimeEnd = TimeEnd;

                Changed(this, args);
            }
        }

        private void UseManualFilterCheckbox_Checked(object sender, RoutedEventArgs e)
        {
            Expander.IsExpanded = true;
        }

        private void UseManualFilterCheckbox_Clicked(object sender, RoutedEventArgs e)
        {
            OnChanged();
        }

        void DateDurationComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            OnChanged();
        }

        void DateBeginPicker_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
        {
            OnChanged();
        }

        void TimeBeginComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (TimeBegin >= TimeEnd)
            {
                TimeEnd = TimeBegin + TimeSpan.FromHours(DeltaHour);
            }

            foreach (ComboBoxItem item in TimeEndComboBox.Items)
            {
                TimeSpan curEndTime = (TimeSpan)item.Tag;
                if (curEndTime <= TimeBegin)
                {
                    item.Foreground = DisabledTimeForeground;
                }
                else
                {
                    item.Foreground = EnabledTimeForeground;
                }
            }

            OnChanged();
        }

        void TimeEndComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (TimeEnd <= TimeBegin )
            {
                TimeBegin = TimeEnd - TimeSpan.FromHours(DeltaHour);
            }

            foreach (ComboBoxItem item in TimeBeginComboBox.Items)
            {
                TimeSpan curBeginTime = (TimeSpan)item.Tag;
                if (curBeginTime >= TimeEnd)
                {
                    item.Foreground = DisabledTimeForeground;
                }
                else
                {
                    item.Foreground = EnabledTimeForeground;
                }
            }

            OnChanged();
        }    

        private void PredefinedPreset_DateThisWeek(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                DateBegin = CurrentWeekMonday;
                DateDuration = TimeSpan.FromDays((double)Duration.Weeks1);
            }
            IsChangeIgnored = false;
            OnChanged();
        }

        private void PredefinedPreset_DateNextWeek(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                DateBegin = CurrentWeekMonday + TimeSpan.FromDays((double)Duration.Weeks1);
                DateDuration = TimeSpan.FromDays((double)Duration.Weeks1);
            }
            IsChangeIgnored = false;
            OnChanged();
        }

        private void PredefinedPreset_DateTwoWeeks(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                DateBegin = DateTime.Today;
                DateDuration = TimeSpan.FromDays((double)Duration.Weeks2);
            }
            IsChangeIgnored = false;
            OnChanged();
        }

        private void PredefinedPreset_TimeFullDay(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                TimeBegin = TimeSpan.FromHours(OpenHour);
                TimeEnd = TimeSpan.FromHours(CloseHour);
            }
            IsChangeIgnored = false;
            OnChanged();
        }

        private void PredefinedPreset_TimeMorning(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                TimeBegin = TimeSpan.FromHours(MorningBegin);
                TimeEnd = TimeSpan.FromHours(MorningEnd);
            }
            IsChangeIgnored = false;
            OnChanged();
        }

        private void PredefinedPreset_TimeMidday(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                TimeBegin = TimeSpan.FromHours(MiddayBegin);
                TimeEnd = TimeSpan.FromHours(MiddayEnd);
            }
            IsChangeIgnored = false;
            OnChanged();
        }

        private void PredefinedPreset_TimeEvening(object sender, RoutedEventArgs e)
        {
            IsChangeIgnored = true;
            {
                TimeBegin = TimeSpan.FromHours(EveningBegin);
                TimeEnd = TimeSpan.FromHours(EveningEnd);
            }
            IsChangeIgnored = false;
            OnChanged();
        }


        public StateDictionary GetState()
        {
            StateDictionary state = new StateDictionary();


            state["IsExpanded"] = Expander.IsExpanded;

            bool isFilterEnabled = UseManualFilterCheckbox.IsChecked ?? false;
            state["IsEnabled"] = isFilterEnabled;

            state["DateBegin"] = DateBegin;
            state["DateDuration"] = DateDuration;
            state["TimeBegin"] = TimeBegin;
            state["TimeEnd"] = TimeEnd;            

            state["IsToday"] = DateBegin == DateTime.Today;
         
            return state;
        }

        public void SetState(StateDictionary state)
        {
            IsChangeIgnored = true;
            try
            {

                Expander.IsExpanded = state.GetBoolean("IsExpanded");
                UseManualFilterCheckbox.IsChecked = state.GetBoolean("IsEnabled");

                DateBegin = state.GetDateTime("DateBegin");
                DateDuration = state.GetTimeSpan("DateDuration");
                TimeBegin = state.GetTimeSpan("TimeBegin");
                TimeEnd = state.GetTimeSpan("TimeEnd");

                bool isToday = state.GetBoolean("IsToday");

                if (isToday)
                {
                    DateBegin = DateTime.Today;
                }
            }           
            finally
            {
                IsChangeIgnored = false;
            }

            OnChanged();
        }


    }
}

